package com.xyh.builder;

import com.xyh.bean.Constants;
import com.xyh.bean.FieldInfo;
import com.xyh.bean.TableInfo;
import com.xyh.utils.DateUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class BuildPo {
    private static final Logger logger = LoggerFactory.getLogger(BuildPo.class);

    public static void execute(TableInfo tableInfo) {
        // 初始化目录和文件
        File poDir = new File(Constants.PATH_PO);
        if (!poDir.exists() && !poDir.mkdirs()) {
            logger.error("创建PO目录失败: {}", Constants.PATH_PO);
            return;
        }

        File poFile = new File(poDir, tableInfo.getBeanName() + ".java");

        // 生成PO类
        try (BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(poFile), StandardCharsets.UTF_8))) {
            writePackage(bw); // 包声明
            writeImports(bw, tableInfo); // 导包区域
            writeClassComment(bw, tableInfo); // 类注释
            writeClassDefinition(bw, tableInfo); // 类定义
            writeFields(bw, tableInfo.getFieldList(), tableInfo.getKeyIndexMap()); // 字段生成
            bw.write("}");
            bw.flush();
            logger.info("生成PO成功: {}", poFile.getAbsolutePath());
        } catch (Exception e) {
            logger.error("生成PO失败: 表名={}", tableInfo.getTableName(), e);
        }
    }

    // 包声明
    private static void writePackage(BufferedWriter bw) throws IOException {
        bw.write("package " + Constants.PACKAGE_PO + ";");
        bw.newLine();
        bw.newLine();
    }

    // 导包区域
    private static void writeImports(BufferedWriter bw, TableInfo tableInfo) throws IOException {
        // 基本导入
        writeBasicImports(bw);

        // 条件导入
        if (tableInfo.getHaveDateTime() || tableInfo.getHaveDate()) {
            writeDateTimeImports(bw);
        }
        if (tableInfo.getHaveBigDecimal()) {
            bw.write("import java.math.BigDecimal;");
            bw.newLine();
        }

        // 忽略字段导入
        if (hasIgnoreField(tableInfo)) {
            bw.write(Constants.IGNORE_BEAN_TO_JSON_EXPRESS_CLASS);
            bw.newLine();
        }

        bw.newLine();
    }

    // 基本导入
    private static void writeBasicImports(BufferedWriter bw) throws IOException {
        String[] imports = {
                "com.baomidou.mybatisplus.annotation.TableName",
                "com.baomidou.mybatisplus.annotation.TableField",
                "com.baomidou.mybatisplus.annotation.TableId",
                "com.baomidou.mybatisplus.annotation.IdType",
                "lombok.*",
                "java.io.Serializable",
                "lombok.AllArgsConstructor",
                "lombok.NoArgsConstructor",
                "lombok.ToString",
                "java.io.Serial"
        };

        for (String imp : imports) {
            bw.write("import " + imp + ";");
            bw.newLine();
        }
    }

    // 日期时间导入
    private static void writeDateTimeImports(BufferedWriter bw) throws IOException {
        bw.write("import java.util.Date;");
        bw.newLine();
        bw.write(Constants.DATE_FORMAT_CLASS);
        bw.newLine();
        bw.write(Constants.DATE_FORMAT_DESERIALIZE_CLASS);
        bw.newLine();
    }

    // 检查是否存在需要忽略的字段
    private static boolean hasIgnoreField(TableInfo tableInfo) {
        for (FieldInfo field : tableInfo.getFieldList()) {
            if (ArrayUtils.contains(Constants.IGNORE_BEAN_TO_JSON_FIELD.split(","), field.getPropertyName())) {
                return true;
            }
        }
        return false;
    }

    // 类注释
    private static void writeClassComment(BufferedWriter bw, TableInfo tableInfo) throws IOException {
        bw.write("/**");
        bw.newLine();
        bw.write(" * " + (null != tableInfo.getComment() ? tableInfo.getComment() : tableInfo.getBeanName()) + " 实体类");
        bw.newLine();
        bw.write(" * ");
        bw.newLine();
        bw.write(" * 对应数据库表: " + tableInfo.getTableName());
        bw.newLine();
        bw.write(" * @author " + Constants.AUTHOR);
        bw.newLine();
        bw.write(" * @date " + DateUtils.format(new Date(), DateUtils.YYYY_MM_DD_HH_MM_SS));
        bw.newLine();
        bw.write(" */");
        bw.newLine();
    }

    // 类定义
    private static void writeClassDefinition(BufferedWriter bw, TableInfo tableInfo) throws IOException {
        bw.write("@TableName(\"" + tableInfo.getTableName() + "\")");
        bw.newLine();
        bw.write("@EqualsAndHashCode(callSuper = false)\n");
        bw.write("@Data");
        bw.newLine();
        bw.write("@AllArgsConstructor");
        bw.newLine();
        bw.write("@NoArgsConstructor");
        bw.newLine();
        bw.write("@ToString");
        bw.newLine();
        bw.write("public class " + tableInfo.getBeanName() + " implements Serializable {");
        bw.newLine();
        bw.newLine();

        // 序列化ID
        bw.write("\t@Serial");
        bw.newLine();
        bw.write("\tprivate static final long serialVersionUID = 1L;");
        bw.newLine();
        bw.newLine();
    }

    // 字段生成
    private static void writeFields(BufferedWriter bw, List<FieldInfo> fieldList,
                                    Map<String, List<FieldInfo>> keyIndexMap) throws IOException {
        String primaryKey = detectPrimaryKey(fieldList, keyIndexMap);

        for (FieldInfo field : fieldList) {
            writeFieldComment(bw, field); // 字段注释
            writeFieldAnnotation(bw, field, primaryKey); // 字段注解
            writeFieldSpecialHandling(bw, field); // 特殊处理
            bw.write("\tprivate " + field.getJavaType() + " " + field.getPropertyName() + ";");
            bw.newLine();
            bw.newLine();
        }
    }

    // 字段注释
    private static void writeFieldComment(BufferedWriter bw, FieldInfo field) throws IOException {
        bw.write("\t/** ");
        bw.newLine();
        if (field.getComment() != null && !field.getComment().isEmpty()) {
            bw.write("\t * " + field.getComment());
            bw.newLine();
        } else {
            bw.write("\t * ");
            bw.newLine();
        }
        bw.write("\t */");
        bw.newLine();
    }

    // 字段注解
    private static void writeFieldAnnotation(BufferedWriter bw, FieldInfo field, String primaryKey) throws IOException {
        if (field.getFieldName().equalsIgnoreCase(primaryKey)) {
            bw.write("\t@TableId(value = \"" + field.getFieldName() + "\", type = IdType.AUTO)");
        } else {
            bw.write("\t@TableField(value = \"" + field.getFieldName() + "\")");
        }
        bw.newLine();
    }

    // 字段特殊处理
    private static void writeFieldSpecialHandling(BufferedWriter bw, FieldInfo field) throws IOException {
        if (Constants.SQL_DATE_TIMES_TYPES.contains(field.getSqlType())) {
            writeDateFormat(bw, DateUtils.YYYY_MM_DD_HH_MM_SS);
        } else if (Constants.SQL_DATE_TYPES.contains(field.getSqlType())) {
            writeDateFormat(bw, DateUtils.YYYY_MM_DD);
        }
        if (ArrayUtils.contains(Constants.IGNORE_BEAN_TO_JSON_FIELD.split(","), field.getPropertyName())) {
            bw.write("\t" + Constants.IGNORE_BEAN_TO_JSON_EXPRESS);
            bw.newLine();
        }
    }

    // 日期格式化
    private static void writeDateFormat(BufferedWriter bw, String pattern) throws IOException {
        bw.write("\t" + String.format(Constants.DATE_FORMAT_EXPRESS, pattern));
        bw.newLine();
        bw.write("\t" + String.format(Constants.DATE_FORMAT_DESERIALIZE, pattern));
        bw.newLine();
    }

    // 主键检测逻辑
    private static String detectPrimaryKey(List<FieldInfo> fieldList,
                                           Map<String, List<FieldInfo>> keyIndexMap) {
        for (Map.Entry<String, List<FieldInfo>> entry : keyIndexMap.entrySet()) {
            if ("PRIMARY".equalsIgnoreCase(entry.getKey())) {
                return entry.getValue().get(0).getFieldName();
            }
        }

        return fieldList.stream()
                .filter(f -> f.getFieldName().toLowerCase().endsWith("id"))
                .findFirst()
                .map(FieldInfo::getFieldName)
                .orElse("");
    }
}